import { expect, test } from '@playwright/test';

import type { Application } from '../models/application';
import { appConfigs } from '../presets';
import type { FakeUser } from '../testUtils';
import { createTestUtils } from '../testUtils';
import { stringPhoneNumber } from '../testUtils/phoneUtils';

test.describe('user profile @generic', () => {
  test.describe.configure({ mode: 'serial' });
  let app: Application;
  let fakeUser: FakeUser;

  test.beforeAll(async () => {
    app = await appConfigs.next.appRouter
      .clone()
      .addFile(
        'src/app/provider.tsx',
        () => `'use client'
import { ClerkProvider } from "@clerk/nextjs";

export function Provider({ children }: { children: any }) {
  return (
    <ClerkProvider>
      {children}
    </ClerkProvider>
  )
}`,
      )
      .addFile(
        'src/app/layout.tsx',
        () => `import './globals.css';
import { Inter } from 'next/font/google';
import { Provider } from './provider';

const inter = Inter({ subsets: ['latin'] });

export const metadata = {
  title: 'Create Next App',
  description: 'Generated by create next app',
};

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <Provider>
      <html lang='en'>
        <body className={inter.className}>{children}</body>
      </html>
    </Provider>
  );
}`,
      )
      .addFile(
        'src/app/hash/user/page.tsx',
        () => `
import { UserProfile, UserButton } from '@clerk/nextjs';

export default function Page() {
  return (
    <div>
      <UserButton />
      <UserProfile routing="hash" />
    </div>
  );
}`,
      )
      .commit();
    await app.setup();
    await app.withEnv(appConfigs.envs.withEmailCodes);
    await app.dev();

    const m = createTestUtils({ app });
    fakeUser = m.services.users.createFakeUser({
      withUsername: true,
      fictionalEmail: true,
      withPhoneNumber: true,
    });
    await m.services.users.createBapiUser({
      ...fakeUser,
      username: undefined,
      phoneNumber: undefined,
    });
  });

  test.afterAll(async () => {
    await fakeUser.deleteIfExists();
    await app.teardown();
  });

  test('user profile with path routing', async ({ page, context }) => {
    const u = createTestUtils({ app, page, context });
    await u.po.signIn.goTo();
    await u.po.signIn.waitForMounted();
    await u.po.signIn.signInWithEmailAndInstantPassword({ email: fakeUser.email, password: fakeUser.password });
    await u.po.expect.toBeSignedIn();

    await u.po.userProfile.goTo();
    await u.po.userProfile.waitForMounted();

    await u.po.userProfile.clickSetUsername();
    await u.page.getByText(/Cancel/i).click();
    await u.page.waitForSelector('.cl-profileSectionContent__username .cl-headerTitle', { state: 'detached' });
  });

  test('user profile with hash routing', async ({ page, context }) => {
    const u = createTestUtils({ app, page, context });
    await u.po.signIn.goTo();
    await u.po.signIn.waitForMounted();
    await u.po.signIn.signInWithEmailAndInstantPassword({ email: fakeUser.email, password: fakeUser.password });
    await u.po.expect.toBeSignedIn();

    await u.page.goToRelative('/hash/user');
    await u.po.userProfile.waitForMounted();

    await u.po.userProfile.clickSetUsername();
    await u.page.getByText(/Cancel/i).click();
    await u.page.waitForSelector('.cl-profileSectionContent__username .cl-headerTitle', { state: 'detached' });
  });

  test('user profile from user button opens actions correctly', async ({ page, context }) => {
    const u = createTestUtils({ app, page, context });
    await u.po.signIn.goTo();
    await u.po.signIn.waitForMounted();
    await u.po.signIn.signInWithEmailAndInstantPassword({ email: fakeUser.email, password: fakeUser.password });
    await u.po.expect.toBeSignedIn();

    await u.page.goToRelative('/');
    await u.page.waitForClerkComponentMounted();

    await u.page.getByRole('button', { name: 'Open user menu' }).click();

    await u.page.getByText(/Manage account/).click();

    await u.po.userProfile.waitForUserProfileModal();

    await u.po.userProfile.clickSetUsername();
    await u.page.getByText(/Cancel/i).click();

    await u.po.userProfile.waitForSectionCardClosed('username');

    await u.po.userProfile.clickAddEmailAddress();
    await u.page.getByText(/Cancel/i).click();

    await u.po.userProfile.waitForSectionCardClosed('emailAddresses');
  });

  test('can update user username', async ({ page, context }) => {
    const u = createTestUtils({ app, page, context });
    await u.po.signIn.goTo();
    await u.po.signIn.waitForMounted();
    await u.po.signIn.signInWithEmailAndInstantPassword({ email: fakeUser.email, password: fakeUser.password });
    await u.po.expect.toBeSignedIn();

    await u.po.userProfile.goTo();
    await u.po.userProfile.waitForMounted();

    await u.po.userProfile.clickSetUsername();
    await u.po.userProfile.waitForSectionCardOpened('username');

    await u.po.userProfile.typeUsername(fakeUser.username);
    await u.page.getByText(/Save/i).click();
    await u.po.userProfile.waitForSectionCardClosed('username');

    const username = await u.page.locator(`.cl-profileSectionItem__username`).innerText();
    expect(username).toContain(fakeUser.username);
  });

  test('update users first and last name', async ({ page, context }) => {
    const u = createTestUtils({ app, page, context });
    await u.po.signIn.goTo();
    await u.po.signIn.waitForMounted();
    await u.po.signIn.signInWithEmailAndInstantPassword({ email: fakeUser.email, password: fakeUser.password });
    await u.po.expect.toBeSignedIn();

    await u.po.userProfile.goTo();
    await u.po.userProfile.waitForMounted();

    await u.po.userProfile.clickToUpdateProfile();
    await u.po.userProfile.waitForSectionCardOpened('profile');

    await u.po.userProfile.typeFirstName('John');
    await u.po.userProfile.typeLastName('Doe');

    await u.page.getByText(/Save/i).click();

    await u.po.userProfile.waitForSectionCardClosed('profile');

    const fullName = await u.page.locator(`.cl-profileSectionItem__profile`).innerText();
    expect(fullName).toContain('John Doe');
  });

  test('add new email address', async ({ page, context }) => {
    const u = createTestUtils({ app, page, context });
    await u.po.signIn.goTo();
    await u.po.signIn.waitForMounted();
    await u.po.signIn.signInWithEmailAndInstantPassword({ email: fakeUser.email, password: fakeUser.password });
    await u.po.expect.toBeSignedIn();

    await u.po.userProfile.goTo();
    await u.po.userProfile.waitForMounted();

    await u.po.userProfile.clickAddEmailAddress();
    await u.po.userProfile.waitForSectionCardOpened('emailAddresses');
    const newFakeEmail = `new-${fakeUser.email}`;
    await u.po.userProfile.typeEmailAddress(newFakeEmail);

    await u.page.getByRole('button', { name: /^add$/i }).click();

    await u.po.userProfile.enterTestOtpCode();

    await expect(
      u.page.locator('.cl-profileSectionItem__emailAddresses').filter({
        hasText: newFakeEmail,
      }),
    ).toContainText(newFakeEmail);
  });

  test('add new phone number', async ({ page, context }) => {
    const u = createTestUtils({ app, page, context });
    await u.po.signIn.goTo();
    await u.po.signIn.waitForMounted();
    await u.po.signIn.signInWithEmailAndInstantPassword({ email: fakeUser.email, password: fakeUser.password });
    await u.po.expect.toBeSignedIn();

    await u.po.userProfile.goTo();
    await u.po.userProfile.waitForMounted();

    await u.po.userProfile.clickAddPhoneNumber();
    await u.po.userProfile.waitForSectionCardOpened('phoneNumbers');
    await u.po.userProfile.typePhoneNumber(fakeUser.phoneNumber);

    await u.page.getByRole('button', { name: /^add$/i }).click();

    await u.po.userProfile.enterTestOtpCode();

    const formatedPhoneNumber = stringPhoneNumber(fakeUser.phoneNumber);

    await expect(u.page.locator('.cl-profileSectionItem__phoneNumbers')).toContainText(formatedPhoneNumber);
  });

  test('add mfa authetiation with phone number', async ({ page, context }) => {
    const u = createTestUtils({ app, page, context });
    await u.po.signIn.goTo();
    await u.po.signIn.waitForMounted();
    await u.po.signIn.signInWithEmailAndInstantPassword({ email: fakeUser.email, password: fakeUser.password });
    await u.po.expect.toBeSignedIn();

    await u.po.userProfile.goTo();
    await u.po.userProfile.waitForMounted();
    await u.po.userProfile.switchToSecurityTab();

    await u.page.getByText(/add two-step verification/i).click();
    await u.page.getByText(/sms code/i).click();

    const formatedPhoneNumber = stringPhoneNumber(fakeUser.phoneNumber);

    await u.page
      .getByRole('button', {
        name: formatedPhoneNumber,
      })
      .click();

    await u.page.getByText(/sms code verification enabled/i).waitFor({
      state: 'visible',
    });
  });

  test('can delete account', async ({ page, context }) => {
    const m = createTestUtils({ app });
    const delFakeUser = m.services.users.createFakeUser({
      withUsername: true,
      fictionalEmail: true,
      withPhoneNumber: true,
    });
    await m.services.users.createBapiUser({
      ...delFakeUser,
      username: undefined,
      phoneNumber: undefined,
    });

    const u = createTestUtils({ app, page, context });
    await u.po.signIn.goTo();
    await u.po.signIn.waitForMounted();
    await u.po.signIn.signInWithEmailAndInstantPassword({ email: delFakeUser.email, password: delFakeUser.password });
    await u.po.expect.toBeSignedIn();

    await u.po.userProfile.goTo();
    await u.po.userProfile.waitForMounted();
    await u.po.userProfile.switchToSecurityTab();

    await u.page
      .getByRole('button', {
        name: /delete account/i,
      })
      .click();

    await u.page.locator('input[name=deleteConfirmation]').fill('Delete account');

    await u.page
      .getByRole('button', {
        name: /delete account/i,
      })
      .click();

    await u.po.expect.toBeSignedOut();

    await u.page.waitForAppUrl('/');

    // Make sure that the session cookie is deleted
    const sessionCookieList = (await u.page.context().cookies()).filter(cookie => cookie.name.startsWith('__session'));

    expect(sessionCookieList.length).toBe(0);
  });

  test('closes the modal after delete', async ({ page, context }) => {
    const m = createTestUtils({ app });
    const delFakeUser = m.services.users.createFakeUser({
      withUsername: true,
      fictionalEmail: true,
      withPhoneNumber: true,
    });
    await m.services.users.createBapiUser({
      ...delFakeUser,
      username: undefined,
      phoneNumber: undefined,
    });

    const u = createTestUtils({ app, page, context });
    await u.po.signIn.goTo();
    await u.po.signIn.waitForMounted();
    await u.po.signIn.signInWithEmailAndInstantPassword({ email: delFakeUser.email, password: delFakeUser.password });
    await u.po.expect.toBeSignedIn();

    await u.page.goToAppHome();

    await u.po.userButton.waitForMounted();
    await u.po.userButton.toggleTrigger();
    await u.po.userButton.triggerManageAccount();

    await u.po.userProfile.waitForUserProfileModal();
    await u.po.userProfile.switchToSecurityTab();

    await u.page
      .getByRole('button', {
        name: /delete account/i,
      })
      .click();

    await u.page.locator('input[name=deleteConfirmation]').fill('Delete account');

    await u.page
      .getByRole('button', {
        name: /delete account/i,
      })
      .click();

    await u.po.expect.toBeSignedOut();
    await u.po.userProfile.waitForUserProfileModal('closed');

    await u.page.waitForAppUrl('/');

    // Make sure that the session cookie is deleted
    const sessionCookieList = (await u.page.context().cookies()).filter(cookie => cookie.name.startsWith('__session'));
    expect(sessionCookieList.length).toBe(0);
  });
});
